#include "cyct_config.h"

#ifdef CYCT_LINUX
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>

#include "cyct_types.h"
#include "cyct_runlog.h"
#include "cyct_debug.h"
#include "cyct_iotty.h"

int cyct_iotty_f_paramset(int fd, int nSpeed, int nBits, char nEvent, int nStop)
{
    struct termios newtio,oldtio;
    if ( tcgetattr( fd,&oldtio) != 0) {
        return -1;
    }
    bzero( &newtio, sizeof( newtio ) );
    newtio.c_cflag |= CLOCAL | CREAD;
    newtio.c_cflag &= ~CSIZE;

    switch( nBits )
    {
    case 7:
        newtio.c_cflag |= CS7;
        break;
    case 8:
        newtio.c_cflag |= CS8;
        break;
    }

    switch( nEvent )
    {
    case 'O':
        newtio.c_cflag |= PARENB;
        newtio.c_cflag |= PARODD;
        newtio.c_iflag |= (INPCK | ISTRIP);
        break;
    case 'E':
        newtio.c_iflag |= (INPCK | ISTRIP);
        newtio.c_cflag |= PARENB;
        newtio.c_cflag &= ~PARODD;
        break;
    case 'N':
        newtio.c_cflag &= ~PARENB;
        break;
    }

    switch( nSpeed )
    {
    case 115200:
        cfsetispeed(&newtio, B115200);
        cfsetospeed(&newtio, B115200);
        break;
    default:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;
    }
    if( nStop == 1 )
    {
        newtio.c_cflag &= ~CSTOPB;
    }
    else if ( nStop == 2 )
    {
        newtio.c_cflag |= CSTOPB;
    }
    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 0;
    tcflush(fd,TCIFLUSH);
    if((tcsetattr(fd,TCSANOW,&newtio))!=0)
    {
        return -1;
    }
    return 0;
}

void* cyct_iotty_open(s32_t i_id_device)
{
    void* p_fd_iotty = NULL;
    
    s32_t i_fd_tty = -1;
    s32_t i_op_status = -1;

    s08_t p_buf_curdevice[128];

    memset(p_buf_curdevice, 0, sizeof(p_buf_curdevice));
    /*sprintf(p_buf_curdevice, "/dev/ttyS%d", i_id_device);*/
    sprintf(p_buf_curdevice, "/dev/ttyUSB%d", i_id_device);
    i_fd_tty = open(p_buf_curdevice, O_RDWR | O_NOCTTY);
    if (-1 == i_fd_tty)
    {
        return NULL;
    }

    if (fcntl(i_fd_tty, F_SETFL, 0)<0)
    {
        close(i_fd_tty);
        return NULL;
    }

    i_op_status = cyct_iotty_f_paramset(i_fd_tty, 115200, 8, 'N', 1);
    if (i_op_status < 0)
    {
        close(i_fd_tty);
        return NULL;
    }

    p_fd_iotty = (void *)i_fd_tty;

    return p_fd_iotty;
}

s32_t cyct_iotty_write(void* p_fd_iotty, u08_t* p_buf_data, s32_t i_len_data)
{
    s32_t i_nb_write = 0;
    i_nb_write = write(p_fd_iotty, p_buf_data, i_len_data);
    return i_nb_write;
}

s32_t cyct_iotty_read(void* p_fd_iotty, u08_t* p_buf_data, s32_t i_len_tryread)
{
    s32_t i_nb_read =0;
    i_nb_read = read(p_fd_iotty, p_buf_data, i_len_tryread);
    return i_nb_read;
}

s32_t cyct_iotty_close(void* p_fd_iotty)
{
    close(p_fd_iotty);
    return 1;
}

s32_t cyct_iotty_rxclean(void* p_fd_iotty)
{
    s32_t i_nb_curread = 0;
    s32_t i_nb_allread = 0;
    u08_t p_buf_curread[128];

    i_nb_curread = cyct_iotty_read(p_fd_iotty, p_buf_curread, sizeof(p_buf_curread));
    while (i_nb_curread > 0)
    {
        i_nb_allread = i_nb_allread + i_nb_curread;
        i_nb_curread = cyct_iotty_read(p_fd_iotty, p_buf_curread, sizeof(p_buf_curread));
    }

    return i_nb_allread;
}
#else /* CYCT_LINUX */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include <windows.h>

#include "cyct_types.h"
#include "cyct_runlog.h"
#include "cyct_debug.h"
#include "cyct_iotty.h"

void* cyct_iotty_open(s32_t i_id_device)
{ 
    void* p_fd_iotty = NULL;
    
    HANDLE hSerialPort = INVALID_HANDLE_VALUE;
    CHAR   szPipeName[64];
    
    DCB dcb;
    COMMTIMEOUTS TimeOuts;
    
    WCHAR wszClassName[64];

    memset(szPipeName, 0, sizeof(szPipeName));
    sprintf(szPipeName, "\\\\.\\COM%lu", (DWORD)(i_id_device));
    memset(wszClassName, 0, sizeof(wszClassName));
    MultiByteToWideChar(CP_ACP, 0, szPipeName, strlen(szPipeName) + 1, wszClassName,
        sizeof(wszClassName) / sizeof(wszClassName[0]));
    hSerialPort = CreateFile((LPCSTR)szPipeName,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);
    
    if (hSerialPort == INVALID_HANDLE_VALUE)
    {
        return NULL;
    }
    
    SetupComm(hSerialPort, 10240, 10240);
    
    TimeOuts.ReadIntervalTimeout = MAXDWORD;
    TimeOuts.ReadTotalTimeoutMultiplier = 0;
    TimeOuts.ReadTotalTimeoutConstant = 0;
    TimeOuts.WriteTotalTimeoutMultiplier = 0;
    TimeOuts.WriteTotalTimeoutConstant = 0;
    SetCommTimeouts(hSerialPort, &TimeOuts);
    
    GetCommState(hSerialPort, &dcb);
    dcb.BaudRate = CBR_115200;
    dcb.ByteSize = 8;
    dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT;
    SetCommState(hSerialPort, &dcb);
    
    FlushFileBuffers(hSerialPort);
    
    p_fd_iotty = (void *)(hSerialPort);

    return p_fd_iotty;
}

/**
 * Write a char to output data stream
 * @param num char to write to output stream
 * @param p_fd_sio struct, contains sio instance data, given by sio_open
 */
s32_t cyct_iotty_send(u08_t i_a_char, void* p_fd_iotty)
{ 
    u32_t i_nb_write = 0;
    DWORD dwNbBytesWritten = 0;
    
    while ((!WriteFile( (HANDLE)(p_fd_iotty), &i_a_char, 1, &dwNbBytesWritten, NULL)) || (dwNbBytesWritten<1));
    
    return 1;
}

/**
 * Read a char from incoming data stream, this call blocks until data has arrived
 * @param p_fd_sio siostatus struct, contains sio instance data, given by sio_open
 * @return char read from input stream
 */
u08_t cyct_iotty_recv(void* p_fd_iotty)
{
    u08_t i_a_char = 0;
    DWORD dwNbBytesReadden = 0;
    
    while (((!ReadFile( (HANDLE)(p_fd_iotty), &i_a_char, 1, &dwNbBytesReadden, NULL)) || (dwNbBytesReadden<1)));
    
    return i_a_char;
}

s32_t cyct_iotty_write(void* p_fd_iotty, u08_t * p_buf_data, s32_t i_len_data)
{ 
    s32_t i_nb_write = 0;

    WriteFile( (HANDLE)(p_fd_iotty), p_buf_data, i_len_data, &i_nb_write, NULL);
    
    return i_nb_write;
}

s32_t cyct_iotty_read(void* p_fd_iotty, u08_t * p_buf_data, s32_t i_buf_size)
{ 
    u32_t i_nb_read = 0;
    DWORD dwNbBytesReadden = 0;
    
    ReadFile((HANDLE)(p_fd_iotty), p_buf_data, i_buf_size, &dwNbBytesReadden, NULL);
    
    return dwNbBytesReadden;
}

s32_t cyct_iotty_close(void* p_fd_iotty)
{ 
    u32_t i_op_status = 0;
    CloseHandle((HANDLE)(p_fd_iotty));
    return i_op_status;
}

s32_t cyct_iotty_rxclean(void* p_fd_iotty)
{
    s32_t i_nb_curread = 0;
    s32_t i_nb_allread = 0;
    u08_t p_buf_curread[128];

    i_nb_curread = cyct_iotty_read(p_fd_iotty, p_buf_curread, sizeof(p_buf_curread));
    while (i_nb_curread > 0)
    {
        i_nb_allread = i_nb_allread + i_nb_curread;
        i_nb_curread = cyct_iotty_read(p_fd_iotty, p_buf_curread, sizeof(p_buf_curread));
    }

    return i_nb_allread;
}
#endif /* CYCT_LINUX */

